unit frmMain;

{$mode objfpc}{$H+}

interface

uses
  Classes, LResources, Forms, Controls, Dialogs, Menus, ActnList, ExtCtrls,
  ComCtrls, StdActns, SynHighlighterTeX, SynEdit, SynEditKeyCmds, LCLType,
  frmEditor, sysutils, process;

type

  { TMainForm }

  TMainForm = class(TForm)
    actnRun: TAction;
    actnMatchBrackets: TAction;
    actnFindReplace: TAction;
    actnSaveAs: TAction;
    actnAutoJustifyParagraph: TAction;
    actnSave: TAction;
    actnNextTab: TAction;
    actnPrevTab: TAction;
    actnClose: TAction;
    actNew:   TAction;
    alActions: TActionList;
    actnOpen: TFileOpen;
    actnQuit: TFileExit;
    actnCopy: TEditCopy;
    actnCut: TEditCut;
    actnPaste: TEditPaste;
    actnSelectAll: TEditSelectAll;
    actnUndo: TEditUndo;
    actnRedo: TEditUndo;
    ApplicationProperties: TApplicationProperties;
    ilActions: TImageList;
    ilGutterMarks: TImageList;
    menuFile: TMenuItem;
    MenuItem1: TMenuItem;
    MenuItem10: TMenuItem;
    MenuItem11: TMenuItem;
    pmOpenTab: TMenuItem;
    pmCloseTab: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem6: TMenuItem;
    MenuItem7: TMenuItem;
    MenuItem8: TMenuItem;
    MenuItem9: TMenuItem;
    miEdit: TMenuItem;
    miNewFile: TMenuItem;
    miCloseFile: TMenuItem;
    miOpenFile: TMenuItem;
    miSaveFileAs: TMenuItem;
    miSaveFile: TMenuItem;
    miQuitApplication: TMenuItem;
    miRecent: TMenuItem;
    MainMenu: TMainMenu;
    TabsPopupMenu: TPopupMenu;
    SaveDialog: TSaveDialog;
    StatusBar: TStatusBar;
    tcFiles: TPageControl;
    Toolbar: TToolBar;
    tbNewFile: TToolButton;
    tbOpenFile: TToolButton;
    tbSaveFile: TToolButton;
    tbSaveFileAs: TToolButton;
    tbCloseFile: TToolButton;
    tbDivider1: TToolButton;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    tbCopy: TToolButton;
    tbCut: TToolButton;
    tbPaste: TToolButton;
    tbUndo: TToolButton;
    tbRedo: TToolButton;
    tbDivider2: TToolButton;
    tbAutoJustifyParagraph: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    procedure actnAutoJustifyParagraphExecute(Sender: TObject);
    procedure actnCloseExecute(Sender: TObject);
    procedure actnCopyExecute(Sender: TObject);
    procedure actnCutExecute(Sender: TObject);
    procedure actNewExecute(Sender: TObject);
    procedure actnFindReplaceExecute(Sender: TObject);
    procedure actnMatchBracketsExecute(Sender: TObject);
    procedure actnNextTabExecute(Sender: TObject);
    procedure actnOpenAccept(Sender: TObject);
    procedure actnPasteExecute(Sender: TObject);
    procedure actnPrevTabExecute(Sender: TObject);
    procedure actnRedoExecute(Sender: TObject);
    procedure actnRunExecute(Sender: TObject);
    procedure actnSaveAsExecute(Sender: TObject);
    procedure actnSaveExecute(Sender: TObject);
    procedure actnSelectAllExecute(Sender: TObject);
    procedure actnUndoExecute(Sender: TObject);
    procedure ApplicationPropertiesException(Sender: TObject; E: Exception);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDropFiles(Sender: TObject; const FileNames: array of string);
    procedure menuFileClick(Sender: TObject);
    procedure miQuitClick(Sender: TObject);
    procedure pmCloseTabClick(Sender: TObject);
    procedure pmOpenTabClick(Sender: TObject);
    procedure TabsPopupMenuPopup(Sender: TObject);
    procedure tcFilesChange(Sender: TObject);
  private
    { private declarations }
    FCurrentEditor: TEditorFrame;
    function ConfirmSave(const i: integer): Boolean;

    // Low-level editor / tabsheet handlers
    function CreateEditor(const AIndex: Integer = -1): Integer;
    function GetEditor(const AIndex: Integer = -1): TEditorFrame;
    procedure CloseEditor(const AIndex: Integer = -1);

    // File action handlers
    procedure NewFile();
    procedure OpenFile(const AFilename: string);
    procedure CloseFile(const AIndex: Integer = -1);
    function SaveFile(const AIndex: Integer = -1): Boolean;
    function SaveFileAs(const AIndex: Integer = -1): Boolean;

    // Recent files
    procedure miRecentFileClick(Sender: TObject);
    procedure UpdateRecentFileMenu;

    // Actions
    procedure EnableOrDisableActions;
  public
    { public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses dmMain, untEditorSheet, frmSearchDialog, frmRun;

{ TMainForm }

procedure TMainForm.actNewExecute(Sender: TObject);
begin
  tcFiles.PageIndex := CreateEditor();
end;

procedure TMainForm.actnFindReplaceExecute(Sender: TObject);
begin
  SearchDialog.Editor := GetEditor();
  SearchDialog.ShowOnTop;
end;

procedure TMainForm.actnMatchBracketsExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.FindMatchingBracket;
end;

procedure TMainForm.actnNextTabExecute(Sender: TObject);
begin
  tcFiles.SelectNextPage(True);
end;

procedure TMainForm.actnCloseExecute(Sender: TObject);
begin
  CloseFile();
end;

procedure TMainForm.actnCopyExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.Copy;
end;

procedure TMainForm.actnCutExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.Cut;
  EnableOrDisableActions;
end;

procedure TMainForm.actnAutoJustifyParagraphExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.AutoJustifyParagraph;
  EnableOrDisableActions;
end;

procedure TMainForm.actnOpenAccept(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to actnOpen.Dialog.Files.Count - 1 do
    OpenFile(actnOpen.Dialog.Files[i]);
  EnableOrDisableActions;
end;

procedure TMainForm.actnPasteExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.Paste;
  EnableOrDisableActions;
end;

procedure TMainForm.actnPrevTabExecute(Sender: TObject);
begin
  tcFiles.SelectNextPage(False);
end;

procedure TMainForm.actnRedoExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.Redo;
  EnableOrDisableActions;
end;

procedure TMainForm.actnRunExecute(Sender: TObject);
begin
  with TRunForm.Create(Self) do
  try
    Editor := GetEditor();
    ShowModal;
  finally
    Free;
  end;
end;

procedure TMainForm.actnSaveAsExecute(Sender: TObject);
begin
  SaveFileAs();
  EnableOrDisableActions;
end;

procedure TMainForm.actnSaveExecute(Sender: TObject);
begin
  SaveFile();
  EnableOrDisableActions;
end;

procedure TMainForm.actnSelectAllExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.SelectAll;
  EnableOrDisableActions;
end;

procedure TMainForm.actnUndoExecute(Sender: TObject);
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  if Assigned(Editor) then Editor.Undo;
  EnableOrDisableActions;
end;

procedure TMainForm.ApplicationPropertiesException(Sender: TObject;
  E: Exception);
begin
  MessageDlg('Error', E.Message, mtError, [mbOK], 0);
end;

procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
var
  i: integer;
begin
  CanClose := False;
  for i := 0 to tcFiles.PageCount - 1 do
    if not ConfirmSave(i) then
      Exit;
  CanClose := True;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FCurrentEditor := nil;
  if dataMain.GetApplicationSetting('StartMaximized', True) then
    WindowState := wsMaximized;
end;

procedure TMainForm.FormDropFiles(Sender: TObject; const FileNames: array of string);
var
  i: integer;
begin
  for i := 0 to Length(FileNames) - 1 do
    OpenFile(FileNames[i]);
end;

procedure TMainForm.menuFileClick(Sender: TObject);
begin
  UpdateActions;
  UpdateRecentFileMenu;
end;

procedure TMainForm.miQuitClick(Sender: TObject);
begin
  Application.Terminate;
end;

procedure TMainForm.pmCloseTabClick(Sender: TObject);
begin
  CloseFile(TabsPopupMenu.Tag);
end;

procedure TMainForm.pmOpenTabClick(Sender: TObject);
begin
  tcFiles.PageIndex := TabsPopupMenu.Tag;
end;

procedure TMainForm.TabsPopupMenuPopup(Sender: TObject);
begin
 // On popup, store the index of the clicked tab in the Tag property
  TabsPopupMenu.Tag := tcFiles.TabIndexAtClientPos(tcFiles.ScreenToClient(Mouse.CursorPos));
end;

procedure TMainForm.tcFilesChange(Sender: TObject);
begin
  EnableOrDisableActions;
end;

procedure TMainForm.UpdateRecentFileMenu;
var RecentFiles: TStringList;
    i: Integer;
    MenuItem: TMenuItem;
begin
  RecentFiles := TStringList.Create;
  try
    dataMain.GetRecentFileList(RecentFiles);
    miRecent.Clear;
    miRecent.Enabled := RecentFiles.Count > 0;
    for i := 0 to RecentFiles.Count - 1 do
    begin
         MenuItem := TMenuItem.Create(miRecent);
         MenuItem.Caption := IntToStr(i + 1) + '. ' + ExtractFileName(RecentFiles[i]);
         if i < 10 then MenuItem.Caption := '&' + MenuItem.Caption;
         MenuItem.Hint := RecentFiles[i];
         MenuItem.OnClick := @miRecentFileClick;
         miRecent.Add(MenuItem);
    end;
  finally
    RecentFiles.Free;
  end;
end;

procedure TMainForm.EnableOrDisableActions;
var Editor: TEditorFrame;
begin
  Editor := GetEditor();
  actnCopy.Enabled := Assigned(Editor) and Editor.CanCopy;
  actnCut.Enabled := Assigned(Editor) and Editor.CanCut;
  actnPaste.Enabled := Assigned(Editor) and Editor.CanPaste;
  actnUndo.Enabled := Assigned(Editor) and Editor.CanUndo;
  actnRedo.Enabled := Assigned(Editor) and Editor.CanRedo;
  actnAutoJustifyParagraph.Enabled := Assigned(Editor);
end;

 // Returns true when the save action was succesful,
 // that is: the file was saved succesfully OR the user clicked No
 // Returns false when error while saving OR the user clicked Cancel
function TMainForm.ConfirmSave(const i: Integer): boolean;
var
  SaveResponse: integer;
  Editor: TEditorFrame;
begin
  Result := True;
  Editor := GetEditor(i);
  if not Assigned(Editor) then Exit;
  if not Editor.Changed then Exit;
  SaveResponse := MessageDlg('Wijzigingen opslaan',
      'Wilt u de wijzigingen in het bestand "' + Editor.DisplayFilename() +
      '"opslaan?', mtConfirmation, mbYesNoCancel, 0);
  if SaveResponse = mrYes then
    Result := SaveFile(i)
  else if SaveResponse = mrNo then
    Result := True
  else if SaveResponse = mrCancel then
    Result := False;
end;

function TMainForm.CreateEditor(const AIndex: Integer = -1): Integer;
var NewTabSheet: TEditorTabSheet;
begin
  NewTabSheet := TEditorTabSheet.Create(tcFiles);
  NewTabSheet.PageControl := tcFiles;
  if (AIndex > -1) then
    NewTabSheet.PageIndex := AIndex;
  Result := tcFiles.IndexOf(NewTabSheet);
  tcFiles.PageIndex := Result;
  NewTabSheet.Editor.GotFocus;
end;

procedure TMainForm.CloseEditor(const AIndex: Integer = -1);
begin
  if (AIndex = -1) then
  begin
    tcFiles.ActivePage.Free;
    tcFiles.ActivePage := nil;
  end;
  if (AIndex > -1) and (AIndex < tcFiles.PageCount) then
    tcFiles.Pages[AIndex].Free;
end;

procedure TMainForm.NewFile();
begin
  tcFiles.PageIndex := CreateEditor();
end;

procedure TMainForm.OpenFile(const AFilename: string);
var i : Integer;
begin
  i := CreateEditor();
  GetEditor(i).OpenFile(AFilename);
  tcFiles.PageIndex := i;
  dataMain.AddToRecentFileList(AFilename);
end;

procedure TMainForm.CloseFile(const AIndex: Integer);
begin
  if ConfirmSave(AIndex) then
    CloseEditor(AIndex);
end;

function TMainForm.SaveFile(const AIndex: Integer): Boolean;
var
  Editor: TEditorFrame;
begin
  Result := False;
  Editor := GetEditor(AIndex);
  if not Assigned(Editor) then Exit;
  if(Editor.Filename = '') then
  begin
    Result := SaveFileAs(AIndex);
    Exit;
  end;
  Result := Editor.SaveFile;
end;

function TMainForm.SaveFileAs(const AIndex: Integer): Boolean;
var
  Editor: TEditorFrame;
begin
  Result := False;
  Editor := GetEditor(AIndex);
  if not Assigned(Editor) then Exit;
  if SaveDialog.Execute then
  begin
    Editor.Filename := SaveDialog.FileName;
    Result := Editor.SaveFile;
    if Result then
      dataMain.AddToRecentFileList(Editor.Filename);
  end;
end;

function TMainForm.GetEditor(const AIndex: Integer = -1): TEditorFrame;
var i : Integer;
begin
  Result := nil;
  if (AIndex = -1) then i := tcFiles.PageIndex else i := AIndex;
  if (i < 0) or (i >= tcFiles.PageCount) then Exit;
  if not (tcFiles.Pages[i] is TEditorTabSheet) then Exit;
  Result := TEditorTabSheet(tcFiles.Pages[i]).Editor;
end;

procedure TMainForm.miRecentFileClick(Sender: TObject);
begin
  OpenFile(TMenuItem(Sender).Hint);
end;

initialization
  {$I frmMain.lrs}

end.

